/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package shaman.storage.impl.stream.gather;

import java.io.IOException;
import java.io.OutputStream;

/**
 *
 * @author dmitriy
 */
public class ScatterOutputStream extends OutputStream {

    public static class DeviceFullException extends IOException {

        public DeviceFullException() {
        }

    }

    private final PartStreamFactory streamFactory;
    private final byte[] buffer;
    private int bufferPos;
    private int blockSize;
    private OutputStream currentStream;

    public ScatterOutputStream(PartStreamFactory factory)
    {
        streamFactory=factory;
        blockSize=factory.getBlockSize();
        buffer=new byte[blockSize];
        currentStream=null;
    }

    @Override
    public void close() throws IOException {
        writeBlock();
        if (currentStream!=null)
            currentStream.close();
    }

    @Override
    public void flush() throws IOException {
        writeBlock();
    }

    @Override
    public void write(byte[] b, int off, int len) throws IOException {
        int availLength=blockSize-bufferPos;
        int copyLen=Math.min(len, availLength);
        System.arraycopy(b, off, buffer, bufferPos, copyLen);
        bufferPos+=copyLen;
        if (bufferPos>=blockSize)
            writeBlock();
        int copyPos;
        for (copyPos=copyLen; copyPos+blockSize<=len; copyPos+=blockSize)
        {
            writeBlock(b, off+copyPos, bufferPos);
        }
        System.arraycopy(b, off+copyPos, buffer, 0, len-copyPos);

    }

    @Override
    public void write(int b) throws IOException {
        buffer[bufferPos++]=(byte) b;
    }

    private void writeBlock() throws IOException {
        writeBlock(buffer, 0, bufferPos);
        bufferPos=0;
    }
    
    private void writeBlock(byte[] data, int offset, int size) throws IOException {
        if (size==0)
            return;
        if (currentStream==null)
        {
            currentStream=streamFactory.getNextOutputStream();
        }
        try {
            currentStream.write(data, offset, size);
        } catch (DeviceFullException ex) {
            currentStream.close();
            currentStream=streamFactory.getNextOutputStream();
            currentStream.write(data, 0, size);
        }
    }

}
